Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secure API #13308

Merged
merged 14 commits into from
Oct 19, 2021
Merged

Secure API #13308

merged 14 commits into from
Oct 19, 2021

Conversation

yaauie
Copy link
Member

@yaauie yaauie commented Oct 12, 2021

Release notes

  • Adds options for securing the Logstash API using TLS and/or HTTP Basic auth.

What does this PR do?

Partial implementation of #13196 adding SSL and Basic auth to the Logstash API

Why is it important/What is the impact to the user?

Empowers users to secure the Logstash API, and to bind to non-loopback interfaces by default when the API is secured.

This in turn will empower future development of the API features.

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have made corresponding change to the default configuration files (and/or docker env variables)
  • I have added tests that prove my fix is effective or that my feature works

Author's Checklist

  • regroups API-related settings under top-level api.*
  • adds api.ssl settings
    • api.ssl.enabled
    • api.ssl.keystore.path and api.ssl.keystore.password
    • api.client_authentication
    • api.ssl.supported_protocols
  • adds api.auth settings
    • api.auth.type: none and basic
    • api.auth.basic.username and api.auth.basic.password
  • api.http.host defaults to localhost when api.ssl.enabled: true and api.auth.type: basic
  • api.http.host defaults to localhost when api.ssl.enabled: true and api.ssl.client_authentication: required

@yaauie
Copy link
Member Author

yaauie commented Oct 14, 2021

@jsvd I would appreciate a WIP-review to make sure this is headed in the right direction.

  • Currently, testing for SSL is entirely manual. I used openssl@3 to generate and sign my keys/certs, but ran into problems exporting those to pkcs12 with openssl@3 and had to fall back to doing so with openssl@1.1. Manual tests show it works with either the .p12 or the jks keystores (certs: shared-ca.tar.gz). These will need to be automated to avoid putting expiring artifacts into source control.
  • The spec calls for api.ssl.client_authorization (none, optional, required), but I'm getting weird hangs in puma that are taking a very long time to chase down -- this feature will likely get pared off for later.
  • The spec also calls for configuring a list of supported TLS protocols. This may be pared off as well, since matching the config param that Elasticsearch has for similar is proving tricky.

@jsvd
Copy link
Member

jsvd commented Oct 14, 2021

Good stuff!! I've been playing with this and I like the details around deprecating the old settings and the warnings when incompatible settings are used.

Regarding authorization, I'm wondering if we should prepare the settings to account for multiple users instead of 1 single user. As soon as we add 1 feature that is more security sensitive like adding a pipeline we'll want separate users. For example, 1 for elastic agent doing monitoring and 1 for operators manipulating pipelines (and maybe even 1 other that can only grab metrics, for diagnostics).

As for encryption, I'm worried about how much Puma's MiniSSL's mini nature will harm our ability to provide a good tls experience to users. Besides the issue you've hit with client authentication, here's a couple of other limitations:

  1. minissl does not allow having separate stores: one for trust, other for node-specific certs/keys. It's normal to have a trust store bundle you can hand out to all nodes, while keystores will be password protected and for each node.

  2. minissl for jruby doesn't support tls1.3. As tls1.1 is deprecated in jdk17 (see jdk.tls.disabledAlgorithms on $JAVA_HOME/conf/security/java.security) we'll want, asap, to also provide tls1.3 support.

  3. maybe other fine tuning settings such as allowing configuration of verification_mode (on top of existing client_authentication).

@yaauie
Copy link
Member Author

yaauie commented Oct 14, 2021

My primary goal with this PR is to provide an interface that we can maintain and grow for the years to come, along-side an implementation that provides some value now.

With that in mind:

Regarding authorization, I'm wondering if we should prepare the settings to account for multiple users instead of 1 single user. As soon as we add 1 feature that is more security sensitive like adding a pipeline we'll want separate users. For example, 1 for elastic agent doing monitoring and 1 for operators manipulating pipelines (and maybe even 1 other that can only grab metrics, for diagnostics).

I agree that a single user isn't going to be enough for features we want to add in the future, but I believe that the interface provides us room to add this in a backward-compatible way if and when those requirements become clear. For example, if we were to add role-based safeguards to different end-points, we could easily add api.auth.basic.role setting to specify the role that our single user gets, defaulting to something read-only like monitoring. We could add additional auth providers that are smarter about assigning roles (separate config file? external RBAC with Elasticsearch?) without breaking users who are using the minimal api.auth.basic implementation provided here.

As for encryption, I'm worried about how much Puma's MiniSSL's mini nature will harm our ability to provide a good tls experience to users. Besides the issue you've hit with client authentication, here's a couple of other limitations:

  1. minissl does not allow having separate stores: one for trust, other for node-specific certs/keys. It's normal to have a trust store bundle you can hand out to all nodes, while keystores will be password protected and for each node.

When we swap out minissl for a different implementation, we can add an option like api.ssl.truststore, and we can default to the value provided to api.ssl.keystore to maintain backward compatibility when a user doesn't provide one.

  1. minissl for jruby doesn't support tls1.3. As tls1.1 is deprecated in jdk17 (see jdk.tls.disabledAlgorithms on $JAVA_HOME/conf/security/java.security) we'll want, asap, to also provide tls1.3 support.
  2. maybe other fine tuning settings such as allowing configuration of verification_mode (on top of existing client_authentication).

Absolutely. This interface is meant to be an ever-expanding subset of the one provided by Elasticsearch's ssl.* options for its API, as we are able. It is not meant to fully-replicate their implementation in one go, but rather to provide a baseline value for some of our users and a path forward for adding value to the rest.

Copy link
Member

@jsvd jsvd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a few comments, mostly minor.

git-wise I'd prefer for this PR to be split into two. wdyt?

One could group all internal changes to settings:

  • docker/data/logstash/env2yaml/env2yaml.go
  • logstash-core/lib/logstash/patches/clamp.rb
  • logstash-core/lib/logstash/patches/puma.rb
  • logstash-core/lib/logstash/settings.rb
  • logstash-core/logstash-core.gemspec
  • maybe parts of logstash-core/lib/logstash/webserver.rb

A second PR would have all the code for the new api settings, deprecating old ones:

  • logstash-core/lib/logstash/agent.rb
  • logstash-core/lib/logstash/runner.rb
  • logstash-core/lib/logstash/environment.rb
  • yml/docs

config/logstash.yml Outdated Show resolved Hide resolved
docs/static/settings-file.asciidoc Show resolved Hide resolved
logstash-core/lib/logstash/webserver.rb Show resolved Hide resolved
logstash-core/lib/logstash/webserver.rb Outdated Show resolved Hide resolved
logstash-core/lib/logstash/runner.rb Show resolved Hide resolved
@yaauie yaauie force-pushed the secure-api branch 3 times, most recently from 7619616 to 5f2e32f Compare October 18, 2021 08:23
Copy link
Member

@jsvd jsvd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The functionality LGTM, I added only 3 nitpicks in this review.
You've mentioned this before and I'd appreciate a test, even if just for the happy path, confirming the correct emission of certificates from a jks/p12 keystore during TLS handshake.

For the assets, if needed we can have a separate repository with pregenerated root certs, server certs, etc, and a github action scheduled to, every 6 months, call the generate script to avoid expirations.

@yaauie yaauie force-pushed the secure-api branch 6 times, most recently from a9ae89c to 263202b Compare October 19, 2021 03:12
@yaauie yaauie marked this pull request as ready for review October 19, 2021 03:13
@yaauie yaauie requested a review from jsvd October 19, 2021 03:13
@yaauie
Copy link
Member Author

yaauie commented Oct 19, 2021

@jsvd I just now saw your comment requesting this be split into two PR's. I've just finished adding the requested tests and getting everything back to green, and will attempt to pare it off into two separate PR's along those lines shortly. I've been maintaining "one thing" per commit, rebasing and squashing as I go, so in theory it shouldn't be too difficult.

@yaauie
Copy link
Member Author

yaauie commented Oct 19, 2021

@jsvd this should be ready for a final review; I've addressed your concerns and appreciate your help in building out the integration tests.

@yaauie yaauie requested a review from jsvd October 19, 2021 16:55
Copy link
Member

@jsvd jsvd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see 4 leftover files from the client cert cleanup, that should be deleted:

qa/integration/fixtures/webserver_certs/generated/client_from_intermediate.jks
qa/integration/fixtures/webserver_certs/generated/client_from_intermediate.p12
qa/integration/fixtures/webserver_certs/generated/client_from_root.jks
qa/integration/fixtures/webserver_certs/generated/client_from_root.p12

Other than that, it all LGTM!! 🎉

@yaauie
Copy link
Member Author

yaauie commented Oct 19, 2021

It looks like I checked in a mixed-generation of certificates, such that the root.crt didn't match the one used to sign the server_*.crt's. I've generated a fresh generation and validated that it all works locally, and have updated this branch to trigger a build.

@yaauie
Copy link
Member Author

yaauie commented Oct 19, 2021

🤦🏼 I was using git add --patch, which blows past binary files, causing the updated .p12 and jks files to not get included. I've updated those and we should be green after that.

@yaauie
Copy link
Member Author

yaauie commented Oct 19, 2021

jenkins test this again please

(ES failed to download in integration test setup)

@yaauie
Copy link
Member Author

yaauie commented Oct 19, 2021

jenkins test this again please

Copy link
Member

@jsvd jsvd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested a few scenarios locally: enabling tls, setting up basic auth, using passwords from logstash keystore, using wrong credentials.

All good, LGTM 🚢

@yaauie yaauie merged commit 15930cc into elastic:master Oct 19, 2021
robbavey pushed a commit to robbavey/logstash that referenced this pull request Oct 20, 2021
* settings: add "deprecated alias" support

A deprecated alias provides a path for renaming a setting.

 - When a deprecated alias is set on its own, a deprecation notice is emitted
   but fetching the canonical setting value will reflect the value set with the
   deprecated alias.
 - When both the canonical setting (new name) and the deprecated alias (old
   name) are specified, it is an error condition.
 - When the value of the deprecated alias is queried, a warning is emitted to
   the logger and only the value explicitly set to the deprecated alias is
   returned.

Additionally, some relevant cleanup is also included:

 - Starting Logstash with invalid settings no longer results in the obtuse "An
   unexpected error occurred" with backtrace and exception data obscuring the
   issue. Instead, a simple message is emitted indicating that the settings are
   invalid along with the originating exception's message.
 - The various settings implementations share a common logger, instead of each
   implementation class providing its own. This is aimed to reduce noise from
   the logs and to ensure specs validating logging do not need to tie so
   closely to implementation details.

* settings: add password-wrapped setting

* settings: make any setting type capable of being nullable

* settings: add `Settings#names` to power programatic iteration

* cli: route CLI-flag deprecations in to deprecation logger

* settings: group API-related settings under `api.*`

retains deprecated aliases, and is fully backward-compatible.

* webserver: cleanup orphaned attr accessors for never-set ivars

* api: pull settings extraction down from agent

This net-no-change refactor introduces a new method `WebServer#from_settings`
that bridges the gap between Logstash settings and Puma-related options, so
that future additions to the API settings don't add complexity to the Agent.

It also has the benefit of initializing the API Rack App and just ONCE, instead
of once per attempted HTTP port.

* api: add optional TLS/SSL

* docs: reference API security settings

* api: when configured securely, bind to all available interfaces by default

* cleanup: remove unused cert artifacts

* tests: generate fresh webserver certificates

* certs: actually add the binary keystores 🤦

Remove references to Java 8 in docs
andsel added a commit that referenced this pull request Nov 15, 2021
…13380)

With #13308 configuration namespace that started with `http.` was renamed to `api.`, this commit fix a usage left behind.
Use the new `api.enabled` setting in one place instead of the deprecated `http.enable`.

Co-authored-by: Ry Biesemeyer <yaauie@users.noreply.github.com>
andsel added a commit to andsel/logstash that referenced this pull request Nov 15, 2021
…lastic#13380)

With elastic#13308 configuration namespace that started with `http.` was renamed to `api.`, this commit fix a usage left behind.
Use the new `api.enabled` setting in one place instead of the deprecated `http.enable`.

Co-authored-by: Ry Biesemeyer <yaauie@users.noreply.github.com>
(cherry picked from commit 88c80eb)
andsel added a commit to andsel/logstash that referenced this pull request Nov 15, 2021
…lastic#13380)

With elastic#13308 configuration namespace that started with `http.` was renamed to `api.`, this commit fix a usage left behind.
Use the new `api.enabled` setting in one place instead of the deprecated `http.enable`.

Co-authored-by: Ry Biesemeyer <yaauie@users.noreply.github.com>
(cherry picked from commit 88c80eb)
andsel added a commit that referenced this pull request Nov 16, 2021
…api.enabled' (#13380) (#13407)

With #13308 configuration namespace that started with `http.` was renamed to `api.`, this commit fix a usage left behind.
Use the new `api.enabled` setting in one place instead of the deprecated `http.enable`.

Co-authored-by: Ry Biesemeyer <yaauie@users.noreply.github.com>
(cherry picked from commit 88c80eb)
andsel added a commit that referenced this pull request Nov 16, 2021
… 'api.enabled' (#13380) (#13408)

With #13308 configuration namespace that started with `http.` was renamed to `api.`, this commit fix a usage left behind.
Use the new `api.enabled` setting in one place instead of the deprecated `http.enable`.

Co-authored-by: Ry Biesemeyer <yaauie@users.noreply.github.com>
(cherry picked from commit 88c80eb)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants